package com.jsbj.web.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.jsbj.base.Constants;
import com.jsbj.base.Result;
import com.jsbj.base.ResultBean;
import com.jsbj.oauth2.exception.Auth2ResponseExceptionTranslator;
import com.jsbj.tools.PinYinUtil;
import com.jsbj.tools.RequestUtil;
import com.jsbj.user.entities.*;
import com.jsbj.user.service.*;
import com.jsbj.web.service.AuthService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.*;

@RestController
@RequestMapping("/oauth")
public class AuthController {
    Logger logger = LoggerFactory.getLogger(getClass());

    private static final String HEADER_TYPE = "Basic ";

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthService authService;

    @Autowired
    private ISysUserService sysUserService;

    private final WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator = new Auth2ResponseExceptionTranslator();

    @GetMapping("/refreshToken") // localhost:7001/auth/user/refreshToken?refreshToken=xxxx
    public Result refreshToken(HttpServletRequest request) {
        try {
            // 获取请求中的刷新令牌
            String refreshToken = request.getParameter("refreshToken");

            Preconditions.checkArgument(StringUtils.isNotEmpty(refreshToken), "刷新令牌不能为空");

            // 获取请求头
            String header = request.getHeader(HttpHeaders.AUTHORIZATION);
            if(header == null || !header.startsWith(HEADER_TYPE)) {
                throw new UnsupportedOperationException("请求头中无client信息");
            }
            // 解析请求头的客户端信息
            String[] tokens = RequestUtil.extractAndDecodeHeader(header);
            assert tokens.length == 2;

            String clientId = tokens[0];
            String clientSecret = tokens[1];

            // 查询客户端信息，核对是否有效
            ClientDetails clientDetails =
                    clientDetailsService.loadClientByClientId(clientId);
            if(clientDetails == null) {
                throw new UnsupportedOperationException("clientId对应的配置信息不存在：" + clientId);
            }
            // 校验客户端密码是否有效
            if( !passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
                throw new UnsupportedOperationException("无效clientSecret");
            }

            // 获取新的认证信息
            return authService.refreshToken(header, refreshToken);
        } catch(Exception e) {
            logger.error("refreshToken={}", e.getMessage(), e);
            return Result.error("新令牌获取失败：" + e.getMessage());
        }


    }

    /**
     * 重写login接口
     *
     * @param principal
     * @param parameters
     * @return
     */
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private TokenEndpoint tokenEndpoint;

    @PostMapping("/token")
    public Result postAccessToken(Principal principal,
                                  @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        //解决生产环境密码错误，不能正确提示中文的问题
        String loginId = MapUtils.getString(parameters, "username");
        String password = MapUtils.getString(parameters, "password");
        //根据用户名和密码查询用户数据
        SysUser sysUser = sysUserService.verificatLoginIdAndPasswrod(loginId,password);
        if(sysUser==null){
            throw new BadCredentialsException("用户名或密码错误");
        }
        OAuth2AccessToken accessToken;
        accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
        Map<String, Object> resultMap = Maps.newLinkedHashMap();
        // token信息
        resultMap.put("access_token", accessToken.getValue());
        resultMap.put("token_type", accessToken.getTokenType());
        resultMap.put("expires_in", accessToken.getExpiresIn());
        resultMap.put("scope", StringUtils.join(accessToken.getScope(), ","));
        resultMap.putAll(accessToken.getAdditionalInformation());
        // 权限信息
//        Collection<? extends GrantedAuthority> authorities =
//                tokenStore.readAuthentication(accessToken).getUserAuthentication().getAuthorities();
//        List<String> list = new ArrayList<>();
//        for (GrantedAuthority authority : authorities) {
//            list.add(authority.getAuthority());
//        }
//        resultMap.put("authorities", list);
        // 保存登录信息
        Map<String, Object> userInfo = MapUtils.getMap(resultMap, "userInfo");
//        sysLoginRecordService.saveLoginRecord(userInfo);
        // 如果从app登录，需要保存设备号
        String equipmentId = MapUtils.getString(parameters, "equipmentId", "");
        if (StringUtils.isNotEmpty(equipmentId)) {
            String equipmentType = MapUtils.getString(parameters, "equipmentIdType", "");
            String equCode = "";
            if ("android".equals(equipmentType)) {
                equCode = "1";
            } else if ("ios".equals(equipmentType)) {
                equCode = "2";
            }
//            sysUserService.updateEquipmentId(MapUtils.getString(userInfo, "uid"), equipmentId, equCode);
        }
        return Result.ok(resultMap);
    }

    // 这里是异常翻译，如果这里不设置，则需要在全局异常处处理
    @ExceptionHandler({Exception.class})
    public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
        return this.exceptionTranslator.translate(e);
    }

    @PostMapping("/tokenRefresh")
    public Result tokenRefresh(@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        Map<String, Object> resultMap = Maps.newLinkedHashMap();
        String userId = MapUtils.getString(parameters, "userId");
        SysUser sysUser = sysUserService.getById(userId);
        if(sysUser!=null) {
            parameters.put("username",sysUser.getLoginId());
            parameters.put("password",sysUser.getPwdDomainCode());
            parameters.put("grant_type","password");
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("jsbj-admin","123456",new ArrayList<>());
            OAuth2AccessToken accessToken;
            accessToken = tokenEndpoint.postAccessToken(authenticationToken, parameters).getBody();

            // token信息
            resultMap.put("access_token", accessToken.getValue());
            resultMap.put("token_type", accessToken.getTokenType());
            resultMap.put("expires_in", accessToken.getExpiresIn());
            resultMap.put("scope", StringUtils.join(accessToken.getScope(), ","));
            Map<String,Object> userInfo = new HashMap<>();
            userInfo.put("uid",userId);
            userInfo.put("username",sysUser.getLoginId());
            userInfo.put("nickName",sysUser.getUserName());
            userInfo.put("imageUrl",sysUser.getImageUrl());
            userInfo.put("mobile",sysUser.getMobile());
            userInfo.put("email",sysUser.getEmail());
            resultMap.put("userInfo",userInfo);
        }
        return Result.ok(resultMap);
    }

    @GetMapping("/getToken")
    public Result getToken(@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        Map<String, Object> resultMap = Maps.newLinkedHashMap();
        String loginId = MapUtils.getString(parameters, "loginId");
        String password = MapUtils.getString(parameters, "password");
//        SysUser sysUser = sysUserService.getById(userId);
        if(StringUtils.isNotEmpty(loginId) && StringUtils.isNotEmpty(password)) {
            parameters.put("username",loginId);
            parameters.put("password",password);
            parameters.put("grant_type","password");
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("jsbj-admin","123456",new ArrayList<>());
            OAuth2AccessToken accessToken;
            accessToken = tokenEndpoint.postAccessToken(authenticationToken, parameters).getBody();

            // token信息
            resultMap.put("access_token", accessToken.getValue());
            resultMap.put("token_type", accessToken.getTokenType());
            resultMap.put("expires_in", accessToken.getExpiresIn());

            LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SysUser::getDisabled, 0);
            queryWrapper.eq(SysUser::getLoginId, loginId);
            queryWrapper.last(" limit 1");
            SysUser sysUser = sysUserService.getOne(queryWrapper);
            resultMap.put("userId",sysUser.getId());
//            resultMap.put("scope", StringUtils.join(accessToken.getScope(), ","));
        }
        return Result.ok(resultMap);
    }

}
